﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using LuaVM.Core;

namespace LuaVM.Helpers
{
	class PluginManual
	{
		public string Name { get; set; }
		public string Target { get; set; }
	}

	class PluginManager
	{
		public delegate void PluginSelectionDelegate(LuaDllPlugin[] SelectedPlugins);

		public static readonly string PluginFolder = AppDomain.CurrentDomain.BaseDirectory + "Plugins";

		public HashSet<LuaDllPlugin> AllPlugins { get; private set; } = new HashSet<LuaDllPlugin>();
		public HashSet<LuaDllPlugin> SelectedPlugins { get; private set; } = new HashSet<LuaDllPlugin>();
		public List<PluginManual> PluginManuals { get; private set; } = new List<PluginManual>();
		public PluginSelectionDelegate SelectionChange { get; set; }

		public PluginManager()
		{
			if (!Directory.Exists(PluginFolder))
			{
				return;
			}

			ScanAllPlusins();
			ScanAllManuals();
		}	
		
		public void Attach(LuaEnv lua)
		{
			foreach (LuaDllPlugin plugin in SelectedPlugins)
			{
				plugin.Attach(lua);
			}
		}

		public void Detach()
		{
			foreach (LuaDllPlugin plugin in SelectedPlugins)
			{
				plugin.Detach();
			}
		}

		public void SetSelectedDllNames(string[] dllNames)
		{
			SelectedPlugins.Clear();

			if (dllNames == null)
			{
				return;
			}

			foreach (string dllName in dllNames)
			{
				LuaDllPlugin plugin = FindByDllName(dllName);
				if (plugin != null)
				{
					SelectedPlugins.Add(plugin);
				}
			}

			SelectionChange?.Invoke(GetSelectedPlugins());
		}		

		public void Select(LuaDllPlugin plugin)
		{
			if (plugin != null && !SelectedPlugins.Contains(plugin))
			{
				SelectedPlugins.Add(plugin);
				SelectionChange?.Invoke(GetSelectedPlugins());
			}			
		}

		public void Deselect(LuaDllPlugin plugin)
		{
			if (plugin != null && SelectedPlugins.Contains(plugin))
			{
				SelectedPlugins.Remove(plugin);
				SelectionChange?.Invoke(GetSelectedPlugins());
			}
		}

		public LuaDllPlugin[] GetSelectedPlugins()
		{
			List<LuaDllPlugin> output = new List<LuaDllPlugin>(SelectedPlugins);
			return output.ToArray();
		}

		public string[] GetSelectedDllNames()
		{
			List<string> output = new List<string>();
			foreach (LuaDllPlugin plugin in SelectedPlugins)
			{
				output.Add(Path.GetFileName(plugin.DllPath));
			}
			output.Sort();
			return output.ToArray();
		}
	
		public void InitializePluginMenu(ToolStripMenuItem menu)
		{
			List<LuaDllPlugin> plugins = new List<LuaDllPlugin>(AllPlugins);			
			plugins.Sort((x, y) => string.Compare(x.Name, y.Name, true));

			menu.DropDownItems.Clear();
			foreach (LuaDllPlugin plugin in plugins)
			{
				ToolStripMenuItem ti = new ToolStripMenuItem();
				ti.Text = plugin.Name;
				ti.Tag = plugin;
				ti.Enabled = true;
				ti.ToolTipText = plugin.DllPath;
				ti.Checked = SelectedPlugins.Contains(plugin);
				ti.Click += Menu_PluginClick;
				menu.DropDownItems.Add(ti);
			}
		}

		public void InitializeManualMenu(ToolStripMenuItem menu)
		{
			menu.DropDownItems.Clear();
			foreach (PluginManual manual in PluginManuals)
			{
				ToolStripMenuItem ti = new ToolStripMenuItem();
				ti.Text = manual.Name;
				ti.Tag = manual;
				ti.Enabled = true;
				ti.ToolTipText = manual.Target;
				ti.Click += Menu_ManualClick;
				menu.DropDownItems.Add(ti);
			}
		}

		private void Menu_PluginClick(object sender, EventArgs e)
		{
			ToolStripMenuItem ti = sender as ToolStripMenuItem;
			LuaDllPlugin plugin = ti.Tag as LuaDllPlugin;
			if (plugin == null)
			{
				return;
			}

			if (SelectedPlugins.Contains(plugin))
			{
				Deselect(plugin);
				ti.Checked = false;
			}
			else
			{
				Select(plugin);
				ti.Checked = true;
			}			
		}

		private void Menu_ManualClick(object sender, EventArgs e)
		{
			ToolStripMenuItem ti = sender as ToolStripMenuItem;
			PluginManual manual = ti.Tag as PluginManual;
			if (manual == null)
			{
				return;
			}

			try
			{
				System.Diagnostics.Process.Start(manual.Target);
			}
			catch (Exception ex)
			{
				MessageForm.Warning(ex.Message);
			}
		}

		private LuaDllPlugin FindByDllName(string dllName)
		{
			if (string.IsNullOrEmpty(dllName))
			{
				return null;
			}

			foreach (LuaDllPlugin plugin in AllPlugins)
			{
				if (string.Compare(plugin.DllPath, PluginFolder + '\\' + dllName, true) == 0)
				{
					return plugin;
				}
			}

			return null;
		}

		private void ScanAllPlusins()
		{
			AllPlugins.Clear();
			DirectoryInfo di = new DirectoryInfo(PluginFolder);
			FileInfo[] files = di.GetFiles("*.dll");
			foreach (FileInfo fi in files)
			{
				LuaDllPlugin plugin;
				try
				{
					plugin = LuaDllPlugin.LoadFromDll(fi.FullName);
				}
				catch
				{
					continue;
				}

				AllPlugins.Add(plugin);
			}
		}

		static readonly string[] AllowedManualExt = { "txt", ".chm", ".htm", ".html", ".doc", ".docx", ".rtf", ".ppt", ".pptx" };

		private void ScanAllManuals()
		{
			PluginManuals.Clear();
			DirectoryInfo di = new DirectoryInfo(PluginFolder);
			foreach (FileInfo fi in di.GetFiles())
			{
				string ext = fi.Extension.ToLower();
				string path = null;
				if (ext == ".link" || ext == ".url")
				{
					path = GetUrlContents(fi.FullName);
				}
				else if (Array.IndexOf(AllowedManualExt, ext) != -1)
				{
					path = fi.FullName;
				}

				if (path == null)
				{
					continue;
				}

				string name = Path.GetFileNameWithoutExtension(fi.Name);
				if (name.ToLower().StartsWith("plugin."))
				{
					name = name.Substring(7);
				}

				PluginManuals.Add(new PluginManual() { Name = name, Target = path });
			}

			PluginManuals.Sort((x, y) => string.Compare(x.Name, y.Name, true));
		}

		static string GetUrlContents(string filePath)
		{
			string url = null;
			try
			{
				url = File.ReadAllText(filePath).Trim();
			}
			catch
			{
			}

			if (string.IsNullOrEmpty(url))
			{
				return null;
			}

			return url;
		}
	}
}
